﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace Tsanie.Image2Cmt {

    enum WorkState {
        Init,
        Running,
        Cancelled,
        Done
    }

    struct Histogram<T> {
        public T value;
        public int count;

        public override string ToString() {
            return this.count + ", " + value;
        }
    }

    static class HistogramModel {
        public static Histogram<T>[] PickUpMax<T>(this Histogram<T>[] array, int count) {
            Histogram<T>[] result = new Histogram<T>[count];
            Array.Copy(array, result, count);
            // 遍历array
            for (int i = count; i < array.Length; i++) {
                Histogram<T> ele = array[i];
                // 遍历result
                for (int j = 0; j < result.Length; j++) {
                    if (ele.count > result[j].count) {
                        result[j] = ele;
                        break;
                    }
                }
            }
            return result;
        }
    }

    class ValueBitmap : IDisposable {

        #region - Private Fields -

        private WorkState _state = WorkState.Init;
        private int _current = 0;
        private int _lines = 0;

        private Histogram<Color>[] _palette = new Histogram<Color>[0];
        private int mostColorCount = 0;
        private int[,] _bitmapData = new int[0, 0];
        private int _width = 0, _height = 0;
        private int _layerWidth = 0, _layerHeight = 0;
        private Dictionary<Color, ColorLayer> _layers = null;

        #endregion

        #region - Properties -

        public WorkState State { get { return _state; } }
        public int Current { get { return _current; } }
        public int LineCount { get { return _lines; } }

        public Histogram<Color>[] Palette { get { return _palette; } }
        public int MostColorCount { get { return mostColorCount; } }
        public int[,] BitmapData { get { return _bitmapData; } }
        public int Width { get { return _width; } }
        public int Height { get { return _height; } }
        public int LayerWidth { get { return _layerWidth; } }
        public int LayerHeight { get { return _layerHeight; } }
        public Dictionary<Color, ColorLayer> Layers { get { return _layers; } }

        #endregion

        #region - IDisposable Members -

        public void Dispose() {
            _palette = null;
            _bitmapData = null;
            ClearLayers();
        }
        private void ClearLayers() {
            if (_layers != null) {
                foreach (KeyValuePair<Color, ColorLayer> pair in _layers) {
                    if (pair.Value.lines != null) {
                        pair.Value.lines.Clear();
                        pair.Value.lines = null;
                    }
                }
                _layers.Clear();
                _layers = null;
            }
        }

        #endregion

        #region - Publc methods -

        public void Stop() {
            _state = WorkState.Cancelled;
        }

        public void ParseBitmap(Bitmap bmp) {
            _state = WorkState.Running;

            Hash<Color> hash = new Hash<Color>();
            int[] histogram = new int[16777216];
            int[,] result = new int[bmp.Height, bmp.Width];
            Histogram<Color>[] entries = null;
            this._width = bmp.Width;
            this._height = bmp.Height;
            this._current = 0;
            // bmp 像素格式
            int bwidth = 3;
            switch (bmp.PixelFormat) {
                //case PixelFormat.Format24bppRgb:
                //    bwidth = 3;
                //    break;
                case PixelFormat.Format32bppArgb:
                    bwidth = 4;
                    break;
                case PixelFormat.Format8bppIndexed:
                    bwidth = 1;
                    entries = bmp.Palette.Entries.ToColorHistogram();
                    break;
            }
            // Get BitmapData
            BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
            int strideOut = data.Stride - _width * bwidth;
            unsafe {
                byte* p = (byte*)data.Scan0;
                fixed (int* ptr = result) {
                    int* pr = ptr;
                    for (int y = 0; y < _height; y++) {
                        for (int x = 0; x < _width; x++) {
                            if (_state != WorkState.Running) { goto cancelled; }
                            if (bwidth == 4) {
                                Color c = ColorModel.FromBgra(*p++, *p++, *p++, *p++);
                                histogram[c.GetHashCode()]++;
                                *pr++ = hash.AddOrGetLocation(c);
                            } else if (bwidth == 1) {
                                *pr++ = *p++;
                            } else {
                                Color c = ColorModel.FromBgr(*p++, *p++, *p++);
                                histogram[c.GetHashCode()]++;
                                *pr++ = hash.AddOrGetLocation(c);
                            }
                        }
                        p += strideOut;
                        this._current = y;
                    }
                }
            }
            bmp.UnlockBits(data);
            this._bitmapData = result;
            if (bwidth == 1) {
                this._palette = entries;
            } else {
                this._palette = hash.ToHistogram();
                for (int i = 0; i < this._palette.Length; i++) {
                    int count = histogram[this._palette[i].value.GetHashCode()];
                    if (count > this.mostColorCount)
                        this.mostColorCount = count;
                    this._palette[i].count = count;
                }
            }
            hash.Dispose();
            histogram = null;
            GC.Collect();

            _state = WorkState.Done;
            return;

        cancelled:
            bmp.UnlockBits(data);
            hash.Dispose();
            histogram = null;
            GC.Collect();
            _state = WorkState.Cancelled;
        }

        public unsafe void CreatePreview(int stepX, int stepY, int ignoreColors, Action<int> paletteAction) {
            _state = WorkState.Running;
            this._current = 0;

            // 垃圾清理
            ClearLayers();

            // 仿色处理
            int count = 0;
            Histogram<Color>[] palette = null;
            if (ignoreColors > 0) {
                // 取调色板中出现频率最高的256个
                // start
                //palette = new Histogram<Color>[this._palette.Length];
                //this._palette.CopyTo(palette, 0);
                //Histogram<Color>[] result = this._palette.PickUpMax(256);
                //for (int i = 0; i < palette.Length; i++) {
                //    Histogram<Color> ele = palette[i];
                //    bool flag = true;
                //    for (int j = 0; j < result.Length; j++) {
                //        if (result[j].value == ele.value) {
                //            flag = false;
                //            break;
                //        }
                //    }
                //    if (flag)
                //        palette[i].value = ColorModel.Empty;
                //}
                palette = this._palette;
            } else {
                palette = this._palette;
            }
            if (paletteAction != null) {
                paletteAction(count);
            }

            this._current = 0;
            // 生成预览像素信息数组
            Dictionary<Color, ColorLayer> ls = new Dictionary<Color, ColorLayer>();
            int lineCount = 0;

            #region - 遍历点 -
            // 遍历
            int x1 = 0, y1 = 0;
            for (int y = 0; y < this._height; y += stepY, y1++) {
                int originx = 0;
                ColorLayer lastlayer = null;
                for (int x = x1 = 0; x < this._width; x += stepX, x1++) {
                    if (_state != WorkState.Running) {
                        palette = null;
                        ls.Clear();
                        GC.Collect();
                        _state = WorkState.Cancelled;
                        return;
                    }
                    int index = this._bitmapData[y, x];
                    Color c = palette[index].value;
                    if (c.A == 0) {
                        // 透明点
                        if (lastlayer != null) {
                            lastlayer.lines.Add(new Line() {
                                index = y1,
                                offset = originx,
                                end = x1
                            });
                            lineCount++;
                            lastlayer = null;
                        }
                        continue;
                    }
                    if (lastlayer != null &&
                        lastlayer.color == c) {
                        // 连续线段
                    } else {
                        if (lastlayer != null) {
                            lastlayer.lines.Add(new Line() {
                                index = y1,
                                offset = originx,
                                end = x1
                            });
                            lineCount++;
                        }
                        if (!ls.TryGetValue(c, out lastlayer)) {
                            lastlayer = new ColorLayer() { color = c };
                            ls.Add(c, lastlayer);
                        }
                        originx = x1;
                    }
                }
                if (lastlayer != null) {
                    lastlayer.lines.Add(new Line() {
                        index = y1,
                        offset = originx,
                        end = x1
                    });
                    lineCount++;
                }
                this._current = y1;
            }
            #endregion

            this._layerWidth = x1;
            this._layerHeight = y1;
            this._lines = lineCount;
            this._layers = ls;

            // 垃圾清理
            palette = null;

            GC.Collect();
            _state = WorkState.Done;
        }

        #endregion
    }

    #region - 图形类 -

    class ColorLayer {
        public Color color = ColorModel.Empty;
        public List<Line> lines = new List<Line>();
    }

    struct Line {
        public int index, offset, end;
    }

    #endregion
}
